#ifndef FFINCLUDE_H
#define FFINCLUDE_H

extern "C" {
    #include "libavutil/avstring.h"
    #include "libavutil/eval.h"
    #include "libavutil/mathematics.h"
    #include "libavutil/pixdesc.h"
    #include "libavutil/imgutils.h"
    #include "libavutil/dict.h"
    #include "libavutil/parseutils.h"
    #include "libavutil/samplefmt.h"
    #include "libavutil/avassert.h"
    #include "libavutil/time.h"
    #include "libavutil/opt.h"
    #include "libavutil/bprint.h"
    #include "libavutil/display.h"
    #include "libavutil/mathematics.h"
    #include "libavutil/cpu.h"
    #include "libavutil/ffversion.h"
    #include "libavutil/version.h"

    #include "libavformat/avformat.h"
    #include "libavdevice/avdevice.h"
    #include "libavcodec/avfft.h"
    #include "libswresample/swresample.h"
    #include "libavresample/avresample.h"
    #include "libswscale/swscale.h"

    #include "libavfilter/avfilter.h"
    #include "libavfilter/buffersink.h"
    #include "libavfilter/buffersrc.h"
    #include "libpostproc/postprocess.h"
}

#include <SDL2/SDL.h>
#include <SDL2/SDL_thread.h>
#include <SDL2/SDL_video.h>

#include <inttypes.h>
#include <math.h>
#include <limits.h>
#include <signal.h>
#include <stdint.h>

#include <QObject>

#define MAX_QUEUE_SIZE (15 * 1024 * 1024)
#define MIN_FRAMES 25
#define EXTERNAL_CLOCK_MIN_FRAMES 2
#define EXTERNAL_CLOCK_MAX_FRAMES 10

/* Minimum SDL audio buffer size, in samples. */
#define SDL_AUDIO_MIN_BUFFER_SIZE 512
/* Calculate actual buffer size keeping in mind not cause too frequent audio callbacks */
#define SDL_AUDIO_MAX_CALLBACKS_PER_SEC 30
/* Step size for volume control in dB */
#define SDL_VOLUME_STEP (0.75)
/* no AV sync correction is done if below the minimum AV sync threshold */
#define AV_SYNC_THRESHOLD_MIN 0.04
/* AV sync correction is done if above the maximum AV sync threshold */
#define AV_SYNC_THRESHOLD_MAX 0.1
/* If a frame duration is longer than this, it will not be duplicated to compensate AV sync */
#define AV_SYNC_FRAMEDUP_THRESHOLD 0.1
/* no AV correction is done if too big error */
#define AV_NOSYNC_THRESHOLD 10.0
/* maximum audio speed change to get correct sync */
#define SAMPLE_CORRECTION_PERCENT_MAX 10
/* external clock speed adjustment constants for realtime sources based on buffer fullness */
#define EXTERNAL_CLOCK_SPEED_MIN  0.900
#define EXTERNAL_CLOCK_SPEED_MAX  1.010
#define EXTERNAL_CLOCK_SPEED_STEP 0.001
/* we use about AUDIO_DIFF_AVG_NB A-V differences to make the average */
#define AUDIO_DIFF_AVG_NB   20
/* polls for possible required screen refresh at least this often, should be less than 1/fps */
#define REFRESH_RATE 0.01
/* NOTE: the size must be big enough to compensate the hardware audio buffersize size */
/* TODO: We assume that a decoded and resampled frame fits into this buffer */
#define SAMPLE_ARRAY_SIZE (8 * 65536)
#define CURSOR_HIDE_DELAY 1000000
#define USE_ONEPASS_SUBTITLE_RENDER 1
#define VIDEO_PICTURE_QUEUE_SIZE 3
#define SUBPICTURE_QUEUE_SIZE 16
#define SAMPLE_QUEUE_SIZE 9
#define FRAME_QUEUE_SIZE FFMAX(SAMPLE_QUEUE_SIZE, FFMAX(VIDEO_PICTURE_QUEUE_SIZE, SUBPICTURE_QUEUE_SIZE))

//视频渲染方式
enum VIDEO_RENDER_TYPE{
    CONTAIN,  //自适应
    FILL,     //拉伸
    CENTER    //居中
};

//音视频同步方式
enum AV_SYNC_TYPE{
    AV_SYNC_AUDIO_MASTER,
    AV_SYNC_VIDEO_MASTER,
    AV_SYNC_EXTERNAL_CLOCK
};

//音频参数
typedef struct AudioParams {
    int freq;
    int channels;
    int64_t channel_layout;
    enum AVSampleFormat fmt;
    int frame_size;
    int bytes_per_sec;
} AudioParams;

//时钟
typedef struct Clock {
    double pts;
    double pts_drift;
    double last_updated;
    double speed;
    int serial;
    int paused;
    int *queue_serial;
} Clock;

//压缩帧
typedef struct MyAVPacketList {
    AVPacket pkt;
    struct MyAVPacketList *next;
    int serial;
} MyAVPacketList;

//链表队列，用于缓存AVPacket
typedef struct PacketQueue {
    bool inited;
    MyAVPacketList *first_pkt, *last_pkt;
    int nb_packets;
    int size;
    int64_t duration;
    int abort_request;
    int serial;
    SDL_mutex *mutex;
    SDL_cond *cond;        //用于阻塞模式读取
    AVPacket* flush_pkt;   //Flush帧，向队列压入此帧会自增serial
} PacketQueue;

//解码后的帧
typedef struct Frame {
    AVFrame *frame;
    AVSubtitle sub;
    int serial;
    double pts;
    double duration;
    int64_t pos;
    int width;
    int height;
    int format;
    AVRational sar;
    int uploaded;
    int flip_v;
} Frame;

//环形队列，用于缓存AVFrame
typedef struct FrameQueue {
    bool inited;        //是否已初始化
    Frame queue[FRAME_QUEUE_SIZE];
    int rindex;         //读所写
    int windex;         //写索引
    int size;           //当前缓存帧数
    int max_size;       //最大缓存帧数
    int keep_last;      //设为1时，队列将始终缓存最后一帧
    int rindex_shown;   //将readIndex偏移一个位置，以实现keep_last
    SDL_mutex *mutex;   //读写锁
    SDL_cond *cond;     //阻塞读写条件
    PacketQueue *pktq;  //关联的Packet队列
} FrameQueue;

int frame_queue_init(FrameQueue *f, PacketQueue *pktq, int max_size, int keep_last);
void frame_queue_destory(FrameQueue *f);
Frame *frame_queue_peek_current(FrameQueue *f);
Frame *frame_queue_peek_next(FrameQueue *f);
Frame *frame_queue_peek_last(FrameQueue *f);
Frame *frame_queue_peek_writable(FrameQueue *f, bool block=true);
Frame *frame_queue_peek_readable(FrameQueue *f, bool block=true);
void frame_queue_push(FrameQueue *f);
void frame_queue_next(FrameQueue *f);
void frame_queue_signal(FrameQueue *f);
int frame_queue_nb_remaining(FrameQueue *f);
int64_t frame_queue_last_pos(FrameQueue *f);
void frame_queue_unref_item(Frame *vp);

int packet_queue_init(PacketQueue *q);
void packet_queue_destroy(PacketQueue *q);
void packet_queue_start(PacketQueue *q,AVPacket* flush_pkt);
void packet_queue_abort(PacketQueue *q);
void packet_queue_flush(PacketQueue *q);
int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block, int *serial);
int packet_queue_put(PacketQueue *q, AVPacket *pkt);
int packet_queue_put_nullpacket(PacketQueue *q, int stream_index);
int packet_queue_put_private(PacketQueue *q, AVPacket *pkt);

void init_clock(Clock *c, int *queue_serial);
double get_clock(Clock *c);
void set_clock_at(Clock *c, double pts, int serial, double time);
void set_clock(Clock *c, double pts, int serial);
void set_clock_speed(Clock *c, double speed);
void sync_clock_to_slave(Clock *c, Clock *slave);

QString avErrorCodeToString(int errCode);

#endif // FFINCLUDE_H
